今回はJavaFXでUIコントロールの選択・フォーカス箇所を取得・設定する方法について確認する。選択やフォーカスの操作については別記事(
その1・
その2・
その3)でも紹介してきたが、本記事では総括的な視点で見ていく。
■ 選択操作
ラジオボタンなどでユーザが選択した項目を取得したり、逆にプログラム上で選択項目を選択する方法について確認する。項目選択に関係してプログラム上で行える操作・設定は以下の3つである。
- ユーザが選択した項目およびそのインデックスを取得
- プログラムで項目を選択
- 選択・選択解除イベント発生時の処理(イベントリスナー)を設定
上記の1~3の操作・設定は、選択操作クラスで行う。選択操作クラスは選択項目クラスのgetToggleGroup関数やgetSelectionModel関数で取得できる。選択項目クラスに対応する選択操作クラスと、その操作用の関数を表1に示す。
表1:選択項目クラスと対応する選択操作クラス、選択取得関数・選択設定関数・バインディングの取得関数の早見表
クラス名 |
選択操作クラス |
選択取得 |
選択設定 |
イベントリスナー用の
バインディング取得 |
RadioButton |
ToggleGroup |
getSelectedTogle() |
selectToggle() |
selectedToggleProperty() |
ChoiceBox |
SingleSelectionModel<T> |
getSelectedIndex()
getSelectedItem()
isEmpty()
isSelected() |
select()
selectFirst()
selectLast()
selectNext()
selectPrevious()
clearAndSelect()
clearSelection()
setSelectedIndex()
setSelectedItem() |
selectedIndexProperty()
selectedItemProperty() |
ComboBox |
ListView |
MultipleSelectionModel
(*単複選択可能) |
getSelectedIndices()
getSelectedItems() |
selectFirst()
selectLast()
selectRange()
selectIndices()
selectAll() |
getSelectedIndices()
getSelectedItems() |
TreeView |
TableView |
TableView.
TableViewSelectionModel
(*単複選択可能)
(*行選択可能) |
isEmpty()
isSelected()
getSelectedCells()
getSelectedIndices()
getSelectedItems() |
select()
selectFirst()
selectLast()
selectNext()
selectPrevious()
selectRange()
selectIndices()
selectAll()
clearAndSelect()
clearSelection() |
getSelectedCells()
getSelectedIndices()
getSelectedItems() |
TreeTableView |
TreeTableView.
TreeTableViewSelectionModel
(*単複選択可能)
(*行選択可能) |
*単複選択可能…SelectionModel関数で単体選択(SelectionMode.SINGLE)か、複数選択(SelectionMode.MULTUPLE)かを選べる
*行選択可能…セル選択(CellSelectionEnabled=true)及び、行選択(CellSelectionEnabled=false)を選べる
選択・選択解除イベント処理用のイベントリスナー登録については、表1のイベントハンドラ用のバインディング取得関数でバインディングを取得後、バインディング変数のaddListener関数でイベントリスナーを登録する形になる。
■ サンプルプログラム
選択操作の動作確認を行うサンプルプログラムを以下に示す。サンプルではボタン押下によりリスト・ビュー内の選択項目が変更され、標準出力に選択項目の情報が出力される。
なお、本記事ではプログラム方法の確認までは行わないが、複数選択の方法などは別記事(
その1・
その2)を参照のこと。また、TableViewおよびTreeTableViewについてはセルの取得方法が特殊だが、コチラに関しては別記事『
JavaFX UIコントロール3(テーブル、ツリーテーブル)』を参照頂きたい。
◇サンプルコード
package application;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestSelection extends Application
{
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage)
{
// フォント色がおかしくなることへの対処
System.setProperty( "prism.lcdtext" , "false" );
// シーングラフの作成
VBox root = new VBox();
ListView<String> listView1 = new ListView<String>();
Button button = new Button( "change select!" );
root.getChildren().addAll( listView1 , button );
// シーンの作成
Scene scene = new Scene( root , 250 , 200 );
// ウィンドウ表示
primaryStage.setScene( scene );
primaryStage.show();
// 初期設定
listView1.setItems( FXCollections.observableArrayList( "1st" , "2nd" , "3rd" ) );
listView1.getSelectionModel().selectFirst(); // 初期選択項目を設定
// ボタン押下時のイベントリスナー登録
EventHandler<ActionEvent> buttonActionFilter =
( event ) -> {
// リスト・ビュー内の項目を自動選択
listView1.getSelectionModel().selectLast();
event.consume();
};
button.addEventHandler( ActionEvent.ACTION , buttonActionFilter );
// リスト・ビューに選択項目変更検出時の
// イベントリスナーを登録
listView1.getSelectionModel().selectedIndexProperty().addListener(
( ov , old , current ) ->
{
// リスト・ビュー内の選択項目を出力
System.out.println( "Selection in the listView is : " + listView1.getSelectionModel().getSelectedIndex() );
}
);
}
}
◇実行結果
「change select!」ボタン押下時の標準出力
Selection in the listView is : 2
◇解説
選択項目の設定は41行目の初期選択項目設定と、ボタン押下時の処理(44行目~49行目)で行っている。ボタン押下により選択項目が変化するとバインディングに登録されたイベントリスナー(54行目~60行目)が起動し、現在の選択項目の上表を標準出力に出力している。
■ フォーカス操作
画面表示可能なクラスは、すべてフォーカス設定用のrequestFocus関数を持っている。以下では、ListViewなどクラス内に更に選択項目があるクラスについて、クラス内部のフォーカスを操作する方法を確認する。プログラム上で行えるフォーカスの操作は、以下の3つの操作・設定である。
- フォーカスがあたっている項目を取得
- 項目にフォーカスを設定
- フォーカス・イベント発生時の処理(イベントリスナー)を設定
上記の1~3の操作・設定は、フォーカス操作クラスで行う。フォーカス操作クラスは該当クラスのgetFocusModel関数で取得できる。また、フォーカス・イベント処理用のイベントリスナー登録については、表2のイベントハンドラ用のバインディング取得関数でバインディングを取得後、バインディング
変数のaddListener関数でイベントリスナーを設定する形となる。
表2:フォーカス位置取得可能なクラスに対応するフォーカス操作用クラス、位置取得/設定関数・バインディングの取得関数の早見表
クラス名 |
フォーカス操作用クラス |
フォーカス
位置取得 |
フォーカス設定 |
イベントリスナー用の
バインディング取得 |
ListView |
FocusModel |
isFocused()
getFocusedIndex()
getFocusedItem()
focusedIndexProperty()
focusedItemProperty() |
focus()
focusNext()
focusPrevious()
requestFocus() |
focusedIndexProperty()
focusedItemProperty() |
TreeView |
TableView |
TableView.
TableViewFocusModel |
isFocused()
getFocusedCell()
focusedCellProperty() |
focus()
focusAboveCell()
focusBelowCell()
focusLeftCell()
focusRightCell()
focusNext()
focusPrevious()
requestFocus() |
focusedCellProperty() |
TreeTableView |
TreeTableView.
TreeTableViewFocusModel |
また、フォーカスの遷移順の指定は自動的に決定されるが、遷移順を変更したい場合にはTraversalEngineを利用するという方法があるらしい(
*4)。ただし、この方法は公式な方法ではないため、将来のアップデートで動作しなくなる可能性があることを考慮してほしいとのこと。
■ サンプルプログラム
フォーカス操作の動作確認を行うサンプルプログラムを以下に示す。サンプルではボタン押下によりフォーカスが移動し、標準出力にフォーカス位置の情報が出力される。
◇サンプルコード
package application;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestFocus extends Application
{
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage)
{
// フォント色がおかしくなることへの対処
System.setProperty( "prism.lcdtext" , "false" );
// シーングラフの作成
VBox root = new VBox();
TextField textField1 = new TextField();
TextField textField2 = new TextField();
ListView<String> listView1 = new ListView<String>();
Button button = new Button( "change focus!" );
root.getChildren().addAll( textField1 , textField2 , listView1 , button );
// シーンの作成
Scene scene = new Scene( root , 250 , 200 );
// ウィンドウ表示
primaryStage.setScene( scene );
primaryStage.show();
// 初期設定
textField1.setPromptText( "input text1" );
textField2.setPromptText( "input text2" );
textField2.requestFocus(); // 初期フォーカスの設定
listView1.setItems( FXCollections.observableArrayList( "1st" , "2nd" , "3rd" ) );
// ボタン押下時のイベントリスナー登録
EventHandler<ActionEvent> buttonActionFilter =
( event ) -> {
// リスト・ビューにフォーカスをあて、
// リスト・ビュー内部のフォーカス位置を変更
listView1.requestFocus();
listView1.getFocusModel().focusNext();
event.consume();
};
button.addEventHandler( ActionEvent.ACTION , buttonActionFilter );
// リスト・ビューにフォーカス変更検出時の
// イベントリスナーを登録
listView1.getFocusModel().focusedIndexProperty().addListener(
( ov , old , current ) ->
{
// リスト・ビュー内のフォーカス位置を出力
System.out.println( "Focus in the listView is : " + listView1.getFocusModel().getFocusedIndex() );
}
);
}
}
◇実行結果
「change focus!」ボタンを5回押した場合の標準出力
Focus in the listView is : 0
Focus in the listView is : 1
Focus in the listView is : 2
◇解説
フォーカス位置の設定は、ボタン押下時に起動するイベントハンドラ内で行っている(49行目~56行目)。ListView::requestFocus関数でリスト・ビュー自身にフォーカスを移動させ、FocusModel::getFocusNext関数でリスト内の次の項目へフォーカスを移動させている(53行目,54行目)。
リストビュー内のフォーカス移動検知は、バインディングへのイベントリスナー登録で行っている(61行目~67行目)。イベントリスナー内では、フォーカスを持つ項目のインデックス番号を出力している(65行目)。ボタンを5回押下しても標準出力が3行しか出力されないのは、4回目以降のボタン押下でフォーカス位置が変更しないためである。
■ 参照
- JavaDoc - クラスSelectionModel
- JavaDoc - クラスMultipleSelectionModel
- JavaDoc - クラスFocusModel
- Stack Over Flow「JavaFX: How to change the focus traversal policy?」
- Stack Over Flow「Equivalent of AWT/Swing transferFocus in JavaFX」